package edu.mse.camel.client.whiteboard.gef.ui.editpart;

import java.util.List;

import org.eclipse.draw2d.ChopboxAnchor;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.EllipseAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.DirectEditPolicy;
import org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.DirectEditRequest;
import org.eclipse.gef.requests.ReconnectRequest;
import org.eclipse.ui.views.properties.IPropertySource;

import edu.mse.camel.client.whiteboard.gef.ui.WhiteBoardUtil;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ChangeTextCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ConnectionCreateCommand;
import edu.mse.camel.client.whiteboard.gef.ui.commands.ConnectionReconnectCommand;
import edu.mse.camel.client.whiteboard.gef.ui.figures.EllipseFigure;
import edu.mse.camel.client.whiteboard.gef.ui.figures.IFigureWithText;
import edu.mse.camel.client.whiteboard.gef.ui.figures.RectangleFigure;
import edu.mse.camel.client.whiteboard.gef.ui.figures.StickyNoteFigure;
import edu.mse.camel.client.whiteboard.gef.ui.properties.ShapePropertySource;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Connection;
import edu.mse.camel.client.whiteboard.gef.whiteboard.EllipticalShape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.RectangularShape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.Shape;
import edu.mse.camel.client.whiteboard.gef.whiteboard.StickyNote;
import edu.mse.camel.client.whiteboard.gef.whiteboard.WhiteboardPackage;

/**
 * EditPart used for Shape instances (more specific for EllipticalShape and
 * RectangularShape instances).
 * <p>
 * This edit part must implement the PropertyChangeListener interface, so it can
 * be notified of property changes in the corresponding model element.
 * </p>
 * 
 * @author darpan
 */
//class ShapeEditPart extends AbstractGraphicalEditPart implements Adapter,
//		NodeEditPart, IWhiteboardEditPart {
class ShapeEditPart  extends BaseEditPart  implements Adapter, NodeEditPart,IWhiteboardEditPart {

	public AbstractGraphicalEditPart getEditPart() {
		return this;
	}

	public ShapeEditPart(Object model) {
		super(model);
	this.setModel(model);
	}

	private ConnectionAnchor anchor;
	private Notifier target;

	/**
	 * Upon activation, attach to the model element as a property change
	 * listener.
	 */
	public void activate() {
		if (!isActive()) {
			hookIntoModel(getCastedModel());
			super.activate();
		}
	}

	private void hookIntoModel(EObject model) {
		if (model != null) {
			model.eAdapters().add(this);
		}
	}

	private void unhookFromModel(EObject model) {
		if (model != null) {
			model.eAdapters().remove(this);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies()
	 */
	protected void createEditPolicies() {
		super.createEditPolicies();
		// allow removal of the associated model element
		installEditPolicy(EditPolicy.COMPONENT_ROLE,
				new ShapeComponentEditPolicy());
		// allow the creation of connections and
		// and the reconnection of connections between Shape instances
		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE,
				new GraphicalNodeEditPolicy() {
					/*
					 * (non-Javadoc)
					 * 
					 * @see
					 * org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy#
					 * getConnectionCompleteCommand
					 * (org.eclipse.gef.requests.CreateConnectionRequest)
					 */
					protected Command getConnectionCompleteCommand(
							CreateConnectionRequest request) {
						ConnectionCreateCommand cmd = (ConnectionCreateCommand) request
								.getStartCommand();
						cmd.setTarget((Shape) getHost().getModel());
						return cmd;
					}

					/*
					 * (non-Javadoc)
					 * 
					 * @see
					 * org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy#
					 * getConnectionCreateCommand
					 * (org.eclipse.gef.requests.CreateConnectionRequest)
					 */
					protected Command getConnectionCreateCommand(
							CreateConnectionRequest request) {
						Shape source = (Shape) getHost().getModel();
						Boolean style = ((Boolean) request.getNewObjectType());
						ConnectionCreateCommand cmd = new ConnectionCreateCommand(
								source, style.booleanValue());
						request.setStartCommand(cmd);
						return cmd;
					}

					/*
					 * (non-Javadoc)
					 * 
					 * @see
					 * org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy#
					 * getReconnectSourceCommand
					 * (org.eclipse.gef.requests.ReconnectRequest)
					 */
					protected Command getReconnectSourceCommand(
							ReconnectRequest request) {
						Connection conn = (Connection) request
								.getConnectionEditPart().getModel();
						Shape newSource = (Shape) getHost().getModel();
						ConnectionReconnectCommand cmd = new ConnectionReconnectCommand(
								conn);
						cmd.setNewSource(newSource);
						return cmd;
					}

					/*
					 * (non-Javadoc)
					 * 
					 * @see
					 * org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy#
					 * getReconnectTargetCommand
					 * (org.eclipse.gef.requests.ReconnectRequest)
					 */
					protected Command getReconnectTargetCommand(
							ReconnectRequest request) {
						Connection conn = (Connection) request
								.getConnectionEditPart().getModel();
						Shape newTarget = (Shape) getHost().getModel();
						ConnectionReconnectCommand cmd = new ConnectionReconnectCommand(
								conn);
						cmd.setNewTarget(newTarget);
						return cmd;
					}
				});
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
	 */
	protected IFigure createFigure() {
		IFigure f = createFigureForModel();
		f.setOpaque(true); // non-transparent figure
		f.setForegroundColor(
				WhiteBoardUtil.createColorFromRGB(getCastedModel().getColor()));
		return f;
	}

	/**
	 * Return a IFigure depending on the instance of the current model element.
	 * This allows this EditPart to be used for both sublasses of Shape.
	 */
	private IFigure createFigureForModel() {
		if (getModel() instanceof EllipticalShape) {
			EllipticalShape ellipse = (EllipticalShape) getModel();
			EllipseFigure e = new EllipseFigure();
			e.setLineWidth(WhiteBoardUtil.FIGURE_WIDTH);
			String str = ellipse.getText();
			if (str != null) {
				e.setText(str);
			}
		 	return e;
		} else if (getModel() instanceof RectangularShape) {
			RectangularShape rectangle = (RectangularShape) getModel();
			RectangleFigure r = new RectangleFigure();
			r.setLineWidth(WhiteBoardUtil.FIGURE_WIDTH);
			String str = rectangle.getText();
			if (str != null) {
				r.setText(str);
			}
		 	return r;
		} else if (getModel() instanceof StickyNote) {
			StickyNote note = (StickyNote) getModel();
			StickyNoteFigure s = new StickyNoteFigure();
			String str = note.getText();
			if (str != null) {
				s.setText(str);
			}
		 	return s;
		}
		else {
			// if Shapes gets extended the conditions above must be updated
			throw new IllegalArgumentException();
		}
	}

	/**
	 * Upon deactivation, detach from the model element as a property change
	 * listener.
	 */
	public void deactivate() {
		if (isActive()) {
			unhookFromModel(getCastedModel());
			super.deactivate();
		}
	}

	private Shape getCastedModel() {
		return (Shape) getModel();
	}

	public Object getAdapter(Class key) {
		if (IPropertySource.class == key)
			return new ShapePropertySource(getModel());
		return super.getAdapter(key);
	}

	protected ConnectionAnchor getConnectionAnchor() {
		if (anchor == null) {
			if (getModel() instanceof EllipticalShape)
				anchor = new EllipseAnchor(getFigure());
			else if (getModel() instanceof RectangularShape)
				anchor = new ChopboxAnchor(getFigure());
			else
				// if Shapes gets extended the conditions above must be updated
				throw new IllegalArgumentException("unexpected model");
		}
		return anchor;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelSourceConnections
	 * ()
	 */
	protected List getModelSourceConnections() {
		return getCastedModel().getSourceConnections();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.editparts.AbstractGraphicalEditPart#getModelTargetConnections
	 * ()
	 */
	protected List getModelTargetConnections() {
		return getCastedModel().getTargetConnections();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef
	 * .ConnectionEditPart)
	 */
	public ConnectionAnchor getSourceConnectionAnchor(
			ConnectionEditPart connection) {
		return getConnectionAnchor();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.NodeEditPart#getSourceConnectionAnchor(org.eclipse.gef
	 * .Request)
	 */
	public ConnectionAnchor getSourceConnectionAnchor(Request request) {
		return getConnectionAnchor();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef
	 * .ConnectionEditPart)
	 */
	public ConnectionAnchor getTargetConnectionAnchor(
			ConnectionEditPart connection) {
		return getConnectionAnchor();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.gef.NodeEditPart#getTargetConnectionAnchor(org.eclipse.gef
	 * .Request)
	 */
	public ConnectionAnchor getTargetConnectionAnchor(Request request) {
		return getConnectionAnchor();
	}

	protected void refreshVisuals() {
		// notify parent container of changed position & location
		// if this line is removed, the XYLayoutManager used by the parent
		// container
		// (the Figure of the ShapesDiagramEditPart), will not know the bounds
		// of this figure
		// and will not draw it correctly.
		Rectangle bounds = new Rectangle(getCastedModel().getX(),
				getCastedModel().getY(), getCastedModel().getWidth(),
				getCastedModel().getHeight());
		figure.setBounds(bounds);
		((GraphicalEditPart) getParent()).setLayoutConstraint(this,
				getFigure(), bounds);
	}

	public void notifyChanged(Notification notification) {
		int type = notification.getEventType();
		int featureId = notification.getFeatureID(WhiteboardPackage.class);
		switch (type) {
		case Notification.SET:
			switch (featureId) {
			case WhiteboardPackage.SHAPE__X:
			case WhiteboardPackage.SHAPE__Y:
			case WhiteboardPackage.SHAPE__WIDTH:
			case WhiteboardPackage.SHAPE__HEIGHT:
				refreshVisuals();
				break;
			case WhiteboardPackage.SHAPE__TEXT:
				((IFigureWithText) getFigure()).setText(notification.getNewStringValue());
				getFigure().getUpdateManager().performUpdate();
				break;
//			case WhiteboardPackage.STICKY_NOTE__TEXT:
//				((StickyNoteFigure)getFigure()).setText(notification.getNewStringValue());
//				getFigure().getUpdateManager().performUpdate();
//				break;					
//			case WhiteboardPackage.ELLIPTICAL_SHAPE__TEXT:
//				((EllipseFigure)getFigure()).setText(notification.getNewStringValue());
//				getFigure().getUpdateManager().performUpdate();
//				break;				
//			case WhiteboardPackage.RECTANGULAR_SHAPE__TEXT:
//				((RectangleFigure)getFigure()).setText(notification.getNewStringValue());
//				getFigure().getUpdateManager().performUpdate();
//				break;				
			}
		case Notification.ADD:
		case Notification.REMOVE:
			switch (featureId) {
			case WhiteboardPackage.SHAPE__SOURCE_CONNECTIONS:
				refreshSourceConnections();
				break;
			case WhiteboardPackage.SHAPE__TARGET_CONNECTIONS:
				refreshTargetConnections();
				break;
			}
		}
	}

	public Notifier getTarget() {
		return target;
	}

	public void setTarget(Notifier newTarget) {
		target = newTarget;
	}

	public boolean isAdapterForType(Object type) {
		return getModel() == type;
	}
	String getDirectEditText() {
		    if (getModel() instanceof StickyNote) {
			   return ((StickyNoteFigure)getFigure()).getText();
		    }else if (getModel() instanceof RectangularShape) {
		    	 return ((RectangleFigure)getFigure()).getText();
		    }else if (getModel() instanceof EllipticalShape) {
		    	 return ((EllipseFigure)getFigure()).getText();
		    }else 
		    	return null;
	}

	protected void handlePropertyChanged(Notification msg) {
		switch (msg.getFeatureID(StickyNote.class)) {
			case WhiteboardPackage.STICKY_NOTE__TEXT:
				refreshVisuals();
				return;
		}
		switch (msg.getFeatureID(EllipticalShape.class)) {
		case WhiteboardPackage.ELLIPTICAL_SHAPE__TEXT:
			refreshVisuals();
			return;
	}
	}
    
	protected DirectEditPolicy createDirectEditPolicy() {
		return new DirectEditPolicy() {
			protected Command getDirectEditCommand(DirectEditRequest request) {
				if (getModel() instanceof StickyNote) {
				    return new ChangeTextCommand((StickyNote)getModel(), 
					(String)request.getCellEditor().getValue());
				}else if (getModel() instanceof RectangularShape) {
				    return new ChangeTextCommand((RectangularShape)getModel(), 
					(String)request.getCellEditor().getValue());
				}else if (getModel() instanceof EllipticalShape) {
					 return new ChangeTextCommand((EllipticalShape)getModel(), 
								(String)request.getCellEditor().getValue());
				}else 
					return null;
				
			}
			protected void showCurrentEditValue(DirectEditRequest request) {
				if (getModel() instanceof StickyNote) {
						((StickyNoteFigure)getFigure()).setText(
							(String)request.getCellEditor().getValue());
					getFigure().getUpdateManager().performUpdate();
				}else if (getModel() instanceof RectangularShape) {
					((RectangleFigure)getFigure()).setText(
							(String)request.getCellEditor().getValue());
					getFigure().getUpdateManager().performUpdate();
				}else if (getModel() instanceof EllipticalShape) {
					((EllipseFigure)getFigure()).setText(
							(String)request.getCellEditor().getValue());
					getFigure().getUpdateManager().performUpdate();
				}
			}
		};
	}
}
